home *** CD-ROM | disk | FTP | other *** search
/ Mac-Source 1994 July / Mac-Source_July_1994.iso / C and C++ / Utilities / Winter Shell 1.0d2 / Source / Libraries / ResourceLib / ResourceLib.c < prev    next >
Encoding:
C/C++ Source or Header  |  1994-01-15  |  15.8 KB  |  627 lines  |  [TEXT/KAHL]

  1. /*    Library to interface to the Resource Manager.
  2.  
  3.     Revision History:
  4.     
  5.     94/01/09 aih
  6.     - removed CATCH handler from ResFileExists
  7.     
  8.     93/12/05 aih
  9.     - fixed error in call to blockmove that could move a dereferenced handle
  10.     
  11.     93/11/20 aih
  12.     - added function to get an indexed resource
  13.     
  14.     93/10/27 aih
  15.     - fixed off-by-one error in ResStrLen
  16.     
  17.     93/03/12 AIH
  18.     - Updated memory limiting
  19.     
  20.     92/03/03 AIH
  21.     - Added function to get a resource by name
  22.     
  23.     91/06/01 AIH
  24.     - Added function to get a resource from the application's resource file
  25.     
  26.     91/05/31 AIH
  27.     - While browsing through the UMPG I discovered that 'actb' and 'dctb'
  28.     resources aren't returned by GetResource due to some weird patch
  29.     to the Resource Manager which was supposed to fix a problem
  30.     in the Dialog Manager (why didn't Apple just patch the Dialog
  31.     Manager?). GetResource returns a handle to a copy of the actual
  32.     'actb' or 'dctb' resource. Since nearly all of my code loads
  33.     resources by calling the function "ResGet" in this file, I applied
  34.     the un-bug-fix to this function, so that ResGet returns a handle
  35.     to the actual resource, not a copy of the resource.
  36.     - Added function to release a resource
  37.     
  38.     91/05/06 AIH
  39.     - Fixed a problem with saving the value of ResLoad, which was caused by casting
  40.     a short into a Boolean
  41.     
  42.     91/04/21-23 AIH
  43.     - Added functions to read strings up to a maximum length
  44.     - Aliases are resolved before a file is opened
  45.     
  46.     91/04/18 AIH
  47.     - The function for verifying the sanity of a resource fork is no longer
  48.     used since I'm not sure that it's 32-bit clean (there's a mask using
  49.     the low three bytes of something that looks like a pointer)
  50.     
  51.     91/04/09 AIH
  52.     - Added function to replace an existing resource
  53.     - More careful about use of ResLoad (added assertions to check it) and
  54.     more careful about purgeable resources
  55.     - Memory is limited before resources are loaded so that memory reserves
  56.     won't be tapped
  57.     
  58.     91/04/01-02 AIH
  59.     - Finished adapting the function for testing the sanity of a resource fork
  60.     - Changed use of ReallocHandle to SetHandleSize
  61.     
  62.     91/03/23 AIH
  63.     - Found TN#232, which explained why we must call StripAddress before
  64.     calling OpenResFile (and by extension HOpenResFile). Incorporated  this
  65.     bug fix, which should fix the very odd problem TMON was reporting whenever
  66.     OpenResFile was called with trap discipline enabled.
  67.     
  68.     91/03/17 AIH
  69.     - Removed 'Get' suffix from most resource functions
  70.     
  71.     91/03/03 AIH
  72.     - Shortened prefix of resource library functions from "Rsrc" to "Res"
  73.     and shortened the names of several functions
  74.     
  75.     91/02/27 AIH
  76.     - Added resource fork sanity checker written by John Norstad for
  77.     Disinfectant
  78.     
  79.     91/01/20 AIH
  80.     - Changed calls from memmove to BlockMove
  81.     
  82.     91/01/05 Ari Halberstadt (AIH)
  83.     - Inserted this standard header in all files */
  84.  
  85. #include <string.h>
  86. #include "LowMemLib.h"
  87. #include "MathLib.h"
  88. #include "MacLib.h"
  89. #include "MemoryLib.h"
  90. #include "StringLib.h"
  91. #include "ResourceLib.h"
  92.  
  93. /* true if a valid resource */
  94. Boolean ResValid(Handle rsrc)
  95. {
  96.     return(HandleValid(rsrc) && HandleIsResource(rsrc));
  97. }
  98.  
  99. /* true if the resource file exists */
  100. Boolean ResFileExists(FileType *fp)
  101. {
  102.     CInfoPBRec pb;
  103.     Boolean exists = false;
  104.     
  105.     if (FileExists(fp)) {
  106.         FileCatalog(fp, &pb);
  107.         exists = (pb.hFileInfo.ioFlRLgLen > 0);
  108.     }
  109.     return(exists);
  110. }
  111.  
  112. /* true if the resource file is already open */
  113. Boolean ResFileIsOpen(FileType *fp)
  114. {
  115.     return(false); // program_note: to do
  116. }
  117.  
  118. /* open the resource file */
  119. FileRefType ResFileOpen(FileType *fp, FilePermType permission)
  120. {
  121.     FileRefType ref = FILE_CLOSED;
  122.     
  123.     require(! ResFileIsOpen(fp));
  124.     FileResolve(fp);
  125.     ResFileValid(fp);
  126.     ref = HOpenResFile(fp->vol, fp->dir,
  127.              (StringPtr) StripAddress((Ptr) fp->pnm), permission);
  128.     FailResError();
  129.     ensure(ref != FILE_CLOSED);
  130.     return(ref);
  131. }
  132.  
  133. /* close the resource file */
  134. void ResFileClose(FileRefType ref)
  135. {
  136.     if (ref != FILE_CLOSED) {
  137.         CloseResFile(ref);
  138.         FailResError();
  139.     }
  140. }
  141.  
  142. /* create the resource file */
  143. void ResFileCreate(FileType *fp)
  144. {
  145.     require(FileValid(fp));
  146.     HCreateResFile(fp->vol, fp->dir, fp->pnm);
  147.     FailResError();
  148.     ensure(ResFileValid(fp));
  149. }
  150.  
  151. /* true if the resource exists */
  152. Boolean ResExists(ResType type, short id)
  153. {
  154.     volatile Boolean load = false;
  155.     Boolean exists = false;
  156.     
  157.     TRY {
  158.         load = GetResLoad();
  159.         SetResLoad(false);
  160.         exists = (GetResource(type, id) != NULL && ! ResError());
  161.     } CLEANUP {
  162.         SetResLoad(load);
  163.     } ENDTRY;
  164.     return(exists);
  165. }
  166.  
  167. /* true if the resource exists in the current resource file */
  168. Boolean ResExists1(ResType type, short id)
  169. {
  170.     volatile Boolean load = false;
  171.     Boolean exists = false;
  172.     
  173.     TRY {
  174.         load = GetResLoad();
  175.         SetResLoad(false);
  176.         exists = (Get1Resource(type, id) != NULL && ! ResError());
  177.     } CLEANUP {
  178.         SetResLoad(load);
  179.     } ENDTRY;
  180.     return(exists);
  181. }
  182.  
  183. /* get the resource, making sure that there's enough memory */
  184. static void DoResGet(Handle rsrc)
  185. {
  186.     size_t size = 0;
  187.     
  188.     /* Turned off resource loading so that when the patch to GetResource
  189.         calls HandToHand it will be passed a nil handle, causing it
  190.         to set a nilHandleErr and return the original handle. We also
  191.         turned of resource loading so we could check the memory situation. */
  192.     require(GetResLoad() == false);
  193.     FailNILRes(rsrc);
  194.     if (! *rsrc) {
  195.         /* resource isn't already in memory */
  196.         size = SizeResource(rsrc);
  197.         FailResError();
  198.         MemCheck(size);
  199.         SetResLoad(true);
  200.         LoadResource(rsrc);
  201.         FailResError();
  202.     }
  203.     SetResLoad(true);
  204.     ensure(GetResLoad());
  205.     ensure(ResValid(rsrc));
  206. }
  207.  
  208. /* get the resource */
  209. Handle ResGet(ResType type, short id)
  210. {
  211.     Handle rsrc = NULL;
  212.     
  213.     require(GetResLoad());
  214.     TRY {
  215.         SetResLoad(false);
  216.         rsrc = GetResource(type, id);
  217.         DoResGet(rsrc);
  218.     } CATCH {
  219.         SetResLoad(true);
  220.     } ENDTRY;
  221.     return(rsrc);
  222. }
  223.  
  224. /* get the resource in the current resource file only */
  225. Handle ResGet1(ResType type, short id)
  226. {
  227.     Handle rsrc = NULL;
  228.     
  229.     require(GetResLoad());
  230.     TRY {
  231.         SetResLoad(false);
  232.         rsrc = Get1Resource(type, id);
  233.         DoResGet(rsrc);
  234.     } CATCH {
  235.         SetResLoad(true);
  236.     } ENDTRY;
  237.     return(rsrc);
  238. }
  239.  
  240. /* get the named resource */
  241. Handle ResGetNamed(ResType type, const CStr255 name)
  242. {
  243.     Str255 pname;
  244.     Handle rsrc = NULL;
  245.  
  246.     require(GetResLoad());
  247.     TRY {
  248.         SetResLoad(false);
  249.         c2pstr(strcpy((char *) pname, name));
  250.         rsrc = GetNamedResource(type, pname);
  251.         DoResGet(rsrc);
  252.     } CATCH {
  253.         SetResLoad(true);
  254.     } ENDTRY;
  255.     return(rsrc);
  256. }
  257.  
  258. /* get the indexed resource */
  259. Handle ResGetIndexed(ResType type, short i)
  260. {
  261.     Handle rsrc = NULL;
  262.  
  263.     require(GetResLoad());
  264.     TRY {
  265.         SetResLoad(false);
  266.         rsrc = GetIndResource(type, i);
  267.         DoResGet(rsrc);
  268.     } CATCH {
  269.         SetResLoad(true);
  270.     } ENDTRY;
  271.     return(rsrc);
  272. }
  273.  
  274. /* Get the resource from the application's resource file, regardless
  275.     of the current resource file. */
  276. Handle ResGetApp(ResType type, short id)
  277. {
  278.     volatile FileRefType ref = FILE_CLOSED;
  279.     Handle rsrc = NULL;
  280.     
  281.     TRY {
  282.         ref = CurResFile();
  283.         FailResError();
  284.         UseResFile(MacAppRefNum());
  285.         FailResError();
  286.         rsrc = ResGet1(type, id);
  287.         UseResFile(ref);
  288.         FailResError();
  289.     } CATCH {
  290.         if (ref != FILE_CLOSED)
  291.             UseResFile(ref);
  292.     } ENDTRY;
  293.     return(rsrc);
  294. }
  295.  
  296. /* Release the resource. Based on notes by John Norstad published with
  297.     the source code to Disinfectant. */
  298. void ResRelease(Handle rsrc)
  299. {
  300.     volatile THz zone = NULL;
  301.     
  302.     require(! rsrc || ResValid(rsrc));
  303.     TRY {
  304.         if (rsrc) {
  305.            zone = GetZone(); FailMemError();
  306.            SetZone(HandleZone(rsrc)); FailMemError();
  307.            ReleaseResource(rsrc); FailResError();
  308.            SetZone(zone); FailMemError();
  309.         }
  310.     } CATCH {
  311.         if (zone)
  312.             SetZone(zone);
  313.     } ENDTRY;
  314. }
  315.  
  316. /* write the resource */
  317. void ResWrite(Handle rsrc)
  318. {
  319.     SignedByte state = 0;
  320.     
  321.     require(ResValid(rsrc));
  322.     state = HandleNoPurge(rsrc);
  323.     ChangedResource(rsrc);
  324.     FailResError();
  325.     WriteResource(rsrc);
  326.     FailResError();
  327.     HandleRestore(rsrc, state);
  328. }
  329.  
  330. /* Add and write the resource to the current resource file. You must call
  331.     ResWrite after calling this function. */
  332. void ResAdd(Handle rsrc, ResType type, short id)
  333. {
  334.     require(HandleValid(rsrc));
  335.     AddResource(rsrc, type, id, (StringPtr) "");
  336.     FailResError();
  337.     ensure(ResValid(rsrc));
  338. }
  339.  
  340. /* set the resource to the handle */
  341. void ResSet(Handle rsrc, ResType type, short id)
  342. {
  343.     require(HandleValid(rsrc));
  344.     if (ResExists1(type, id))
  345.         ResRemove(type, id);
  346.     ResAdd(rsrc, type, id);
  347.     ResWrite(rsrc);
  348.     ensure(ResValid(rsrc));
  349. }
  350.     
  351. /* remove the specified resource from the current resource file */
  352. void ResRemove(ResType type, short id)
  353. {
  354.     Handle rsrc = NULL;
  355.     volatile Boolean load = false;
  356.     
  357.     TRY {
  358.         load = GetResLoad();
  359.         SetResLoad(false);
  360.         rsrc = Get1Resource(type, id);
  361.         FailNILRes(rsrc);
  362.         SetResLoad(load);
  363.         RmveResource(rsrc);
  364.         FailResError();
  365.     } CATCH {
  366.         SetResLoad(load);
  367.     } ENDTRY;
  368. }
  369.  
  370. /* Read the first n bytes of the resource with the given type and id
  371.     into the given pointer. */
  372. void ResPtr(ResType type, short id, void *data, size_t n)
  373. {
  374.     Handle rsrc = NULL;
  375.     
  376.     require(MemValid(data));
  377.     require(n >= 0);
  378.     rsrc = ResGet(type, id);
  379.     if (SizeResource(rsrc) < n)
  380.         n = SizeResource(rsrc);
  381.     BlockMove(*rsrc, data, n);
  382. }
  383.  
  384. /* Set the resource to the first n bytes of the data. If the resource exists
  385.     it is resized to hold the data, otherwise it is created. The resource is set
  386.     in the current resource file only. */
  387. void ResPtrSet(ResType type, short id, void *data, size_t n)
  388. {
  389.     volatile Handle rsrc = NULL;
  390.     
  391.     require(MemValid(data));
  392.     require(n >= 0);
  393.     TRY {    
  394.         if (ResExists1(type, id)) {
  395.             /* modify existing resource */
  396.             rsrc = ResGet1(type, id);
  397.             HandleSizeSet(rsrc, n);
  398.         }
  399.         else {
  400.             /* add new resource */
  401.             rsrc = HandleBegin(n);
  402.             ResAdd(rsrc, type, id);
  403.         }
  404.         BlockMove(data, *rsrc, n);
  405.         ResWrite(rsrc);
  406.     } CLEANUP {
  407.         HandleEnd(rsrc);
  408.     } ENDTRY;
  409. }
  410.  
  411. /* get the 'STR ' resource */
  412. void ResString(short id, CStr255 str)
  413. {
  414.     require(StrValid(str, -1));
  415.     *str = 0;
  416.     ResPtr('STR ', id, str, sizeof(CStr255));
  417.     p2cstr((StringPtr) str);
  418.     ensure(StrValid(str, sizeof(CStr255)));
  419. }
  420.  
  421. /* get the first 'len' characters of the string */
  422. void ResStringLen(short id, char *str, short len)
  423. {
  424.     CStr255 bigstr;
  425.     
  426.     require(len >= 0 && len < sizeof(CStr255));
  427.     ResString(id, bigstr);
  428.     strncpy(str, bigstr, len);
  429.     str[len] = 0;
  430.     ensure(StrValid(str, len));
  431. }
  432.  
  433. /* set the 'STR ' resource, adding it if it doesn't already exist */
  434. void ResStringSet(short id, const CStr255 str)
  435. {
  436.     Str255 pstr;
  437.     
  438.     require(StrValid(str, sizeof(CStr255)));
  439.     c2pstr(strcpy((char*)pstr, str));
  440.     ResPtrSet('STR ', id, pstr, *pstr + 1);
  441. }
  442.  
  443. /* get the n'th string of the 'STR#' resource with the given ID */
  444. void ResStr(short id, short n, CStr255 str)
  445. {
  446.     require(n >= 0);
  447.     *str = 0;
  448.     if (n > 0) {
  449.         GetIndString((StringPtr) str, id, n);
  450.         FailResError();
  451.     }
  452.     p2cstr((StringPtr) str);
  453.     ensure(StrValid(str, sizeof(CStr255)));
  454. }
  455.  
  456. /* get the first len-1 characters of the string */
  457. void ResStrLen(short id, short n, char *str, short len)
  458. {
  459.     CStr255 bigstr;
  460.  
  461.     require(StrValid(str, -1));
  462.     require(0 < len && len <= sizeof(CStr255));
  463.     ResStr(id, n, bigstr);
  464.     strncpy(str, bigstr, len);
  465.     str[len-1] = 0;
  466.     ensure(StrValid(str, len));
  467. }
  468.  
  469. /* turn the resource's attribute on or off */
  470. void ResAttributeSet(Handle rsrc, short attr, Boolean on)
  471. {
  472.     short attributes;
  473.     
  474.     require(ResValid(rsrc));
  475.     attributes = GetResAttrs(rsrc);
  476.     FailResError();
  477.     if (on)
  478.         attributes |= attr;
  479.     else
  480.         attributes &= ~attr;
  481.     SetResAttrs(rsrc, attributes);
  482.     FailResError();
  483. }
  484.  
  485. #ifndef NOT_USED
  486.  
  487. Boolean ResFileValid(FileType *fp)
  488. {
  489.     /* this stub will always succeed */
  490.     return(true);
  491. }
  492.  
  493. #else /* NOT_USED */
  494.     
  495.     /* This function appears to work, but I don't understand it, and,
  496.         worse, I fear it may not be 32-bit clean. In the interests of
  497.         compatability this function will not be used. */
  498.  
  499. /* Check a Resource File's Sanity. Adapted from source code to
  500.     Disinfectant, written by John Norstad.
  501.     
  502.     This routine checks the sanity of a resource file by scanning the entire
  503.     resource map. The Resource Manager does not do error checking, and can
  504.     bomb or hang if you use it to open a damaged resource file. This routine
  505.     can be called first to precheck the file. If the resource map is damaged
  506.     mapReadErr is set. 
  507.     
  508.     This function appears prone to compatability problems since it examines
  509.     data structures "owned" by the Resource Manager. Also, many of the
  510.     checks are fairly long sequences of logical and's, so I'm not sure
  511.     John wrote this function correctly (I already found a few errors). */
  512. OSErr ResFileValid(FileType *fp)
  513. {
  514.     long                    count;            /* number of bytes to read */
  515.     long                    logEOF;            /* logical EOF */
  516.     unsigned long        dataLWA;            /* offset in file of data end */
  517.     unsigned long        mapLWA;            /* offset in file of map end */
  518.     unsigned short        typeFWA;            /* offset from map begin to type list */
  519.     unsigned short        nameFWA;            /* offset from map begin to name list */
  520.     unsigned char        *pType;            /* pointer into type list */
  521.     unsigned char        *pName;            /* pointer to start of name list */
  522.     unsigned char        *pMapEnd;        /* pointer to end of map */
  523.     short                    nType;            /* number of resource types in map */
  524.     unsigned char        *pTypeEnd;        /* pointer to end of type list */
  525.     short                    nRes;                /* number of resources of given type */
  526.     unsigned short        refFWA;            /* offset from type list to ref list */
  527.     unsigned char        *pRef;            /* pointer into reference list */
  528.     unsigned char        *pRefEnd;        /* pointer to end of reference list */
  529.     unsigned short        resNameFWA;        /* offset from name list to resource name */
  530.     unsigned char        *pResName;        /* pointer to resource name */
  531.     unsigned long        resDataFWA;        /* offset from data begin to resource data */
  532.     Ptr                    map = NULL;        /* pointer to resource map */
  533.     Boolean                mapOK = false;    /* true if map is sane */
  534.     struct {
  535.         unsigned long        dataFWA;        /* offset in file of data */
  536.         unsigned long        mapFWA;        /* offset in file of map */
  537.         unsigned long        dataLen;        /* data area length */
  538.         unsigned long        mapLen;        /* map area length */
  539.     } header;
  540.     
  541.     TRY {
  542.         memclr(&header, sizeof(header));
  543.         if (mapOK) return;
  544.  
  545.         /* open the resource file */
  546.         FileOpenRes(fp, fsRdPerm);
  547.         
  548.         /* get the logical eof */
  549.         logEOF = FileSize(fp);
  550.         if (logEOF == 0) {
  551.             FileClose(fp);
  552.             return;
  553.         }
  554.         
  555.         /* read and validate the resource header */
  556.         count = FileRead(fp, sizeof(header), &header);
  557.         dataLWA = header.dataFWA + header.dataLen;
  558.         mapLWA = header.mapFWA + header.mapLen;
  559.         mapOK = (count == sizeof(header) &&
  560.                     header.mapLen > 28 &&
  561.                     header.dataFWA < 0x01000000 &&
  562.                     header.mapFWA < 0x01000000 &&
  563.                     dataLWA <= logEOF &&
  564.                     mapLWA <= logEOF &&
  565.                     (dataLWA <= header.mapFWA || mapLWA <= header.dataFWA));
  566.         if (! mapOK) FailOSErr(mapReadErr);
  567.  
  568.         /* read the resource map */
  569.         map = PtrBegin(header.mapLen);
  570.         FileSeek(fp, fsFromStart, header.mapFWA);
  571.         count = FileRead(fp, header.mapLen, map);
  572.  
  573.         /* verify the type list and name list offsets */
  574.         typeFWA = *(unsigned short *) (map + 24);
  575.         nameFWA = *(unsigned short *) (map + 26);
  576.         mapOK = (typeFWA == 28 &&
  577.                     nameFWA >= typeFWA &&
  578.                     nameFWA <= header.mapLen &&
  579.                     ! (typeFWA & 1) && ! (nameFWA & 1));
  580.         if (! mapOK) FailOSErr(mapReadErr);
  581.     
  582.         /* verify the type list, reference lists, and name list */
  583.         pType = (unsigned char *) (map + typeFWA);
  584.         pName = (unsigned char *) (map + nameFWA);
  585.         pMapEnd = (unsigned char *) (map + header.mapLen);
  586.         nType = *(short *) pType + 1;
  587.         pType += 2;
  588.         pTypeEnd = pType + (nType << 3);
  589.         mapOK = (pTypeEnd <= pMapEnd);
  590.         if (! mapOK) FailOSErr(mapReadErr);
  591.         while (pType < pTypeEnd) {
  592.             nRes = *(short*)(pType+4) + 1;
  593.             refFWA = *(unsigned short*)(pType+6);
  594.             pRef = (unsigned char *) (map + typeFWA + refFWA);
  595.             pRefEnd = pRef + 12*nRes;
  596.             mapOK = (pRef >= pTypeEnd &&
  597.                         pRef < pName && 
  598.                         ! (refFWA & 1));
  599.             if (! mapOK) FailOSErr(mapReadErr);
  600.             while (pRef < pRefEnd) {
  601.                 resNameFWA = *(unsigned short*)(pRef+2);
  602.                 if (resNameFWA != 0xFFFF) {
  603.                     pResName = pName + resNameFWA;
  604.                     mapOK = (pResName + *pResName < pMapEnd);
  605.                     if (! mapOK) FailOSErr(mapReadErr);
  606.                 }
  607.                 resDataFWA = *(unsigned long *) (pRef+4) & 0x00FFFFFF;
  608.                 mapOK = ((header.dataFWA + resDataFWA) < dataLWA);
  609.                 if (! mapOK) FailOSErr(mapReadErr);
  610.                 pRef += 12;
  611.             }
  612.             pType += 8;
  613.         }
  614.         ensure(mapOK);
  615.     } CLEANUP {
  616.         FileClose(fp);
  617.         PtrEnd(header.mapLen);
  618.     } CATCH {
  619.         if (FailReason() == fnfErr) {
  620.             mapOK = true;
  621.             RETRY;
  622.         }
  623.     } ENDTRY;
  624. }
  625.  
  626. #endif /* NOT_USED */
  627.